#
# GSTarget.py
# Created by Gerikes for GerikSpells
#

from CvPythonExtensions import *

gc = CyGlobalContext()

import GerikSpellsInterface

import CvUtil
import copy

class GSBase:
    # All targets have these
    def __init__(self, pTargettableCallback):
        self.m_pData = None
        self.m_pTargettableCallback = pTargettableCallback

    def getData1(self):
        return -1

    def getData2(self):
        return -1

    def getData3(self):
        return -1

    def getTarget(self):
        return self.m_pData

    def setTarget(self, pData):
        self.m_pData = pData

    # isPlotTargetPolicy is overridden by plot targets.
    def isPlotTargetPolicy(self):
        return False

    def isCurrentTargetValid(self, pCastingGroup, pCastingPlot):
        return len(self.getAffectedTargetList(pCastingGroup, pCastingPlot)) > 0
        

    def isTargettable(self, pCastingGroup, pCastingPlot, pPotentialTarget, bVisible):
        if (self.m_pTargettableCallback):
            return self.m_pTargettableCallback(pCastingGroup, pCastingPlot, pPotentialTarget, bVisible)

        return True

    # Typically, getSecondaryTargetList will want to get both the affected
    # and unaffected target list. So, it's preferable that we do it all at
    # once to avoid iterating through all the possible targets twice.
    # If it is only necessary to have one list or the other, these
    # are helper functions that can do it for us. However, it's a bad
    # idea to call getAffectedTargetList right after getUnaffectedTargetList.
    def getAffectedTargetList(self, pCastingGroup, pCastingPlot):
        return self.getSecondaryTargetList(pCastingGroup, pCastingPlot)[0]

    def getUnaffectedTargetList(self, pCastingGroup, pCastingPlot):
        return self.getSecondaryTargetList(pCastingGroup, pCastingPlot)[1]

    def getSecondaryTargetList(self, pCastingGroup, pCastingPlot):
        raise NotImplementedError

class GSNoTarget(GSBase):
    def __init__(self):
        GSBase.__init__(self, None)

    def setTargetWithData(self, iData1, iData2, iData3):
        return

    def parseInfo(self, bStrategyOption):
        return ""

    def getBars(self):
        return []

    def getSecondaryTargetList(self):
        return []
        

PLOT_RANGE_AFFECT_RULE_SELECTED = 0
PLOT_RANGE_AFFECT_RULE_GLOBAL = 1
PLOT_RANGE_AFFECT_RULE_INDIVIDUAL = 2

class GSPlotTarget(GSBase):
    def __init__(self, pTargettableCallback):
        GSBase.__init__(self, pTargettableCallback)

        self.m_bCheckVisible = True
        self.m_bCheckRange = False

        self.m_iRangeAffectRule = PLOT_RANGE_AFFECT_RULE_SELECTED
        self.m_pRangeMakerCallback = None
        self.m_pRangePlotList = None # Will be set during the stage's activation.

    # GERIKESTODO: Does anyone use this?
    def isPlotTargetPolicy(self):
        return True

    def isTargettable(self, pCastingGroup, pCastingPlot,  pPotentialTarget, bVisible):
        if (self.m_bCheckVisible):
            if not pPotentialTarget.isVisible(pCastingGroup.getTeam(), False):
                return False

        if (self.m_bCheckRange):
            iPotTargetX = pPotentialTarget.getX()
            iPotTargetY = pPotentialTarget.getY()
            bFound = False
            for pPlot in self.getRangePlotList(pCastingGroup, pCastingPlot):
                if (iPotTargetX == pPlot.getX() and iPotTargetY == pPlot.getY()):
                    bFound = True
                    break

            if (not bFound):
                return False

        if (self.m_pTargettableCallback):
            return self.m_pTargettableCallback(pCastingGroup, pCastingPlot, pPotentialTarget, bVisible)

        return True

    def getCheckRange(self):
        return self.m_bCheckRange

    def setCheckRange(self, bCheck):
        self.m_bCheckRange = bCheck

    def getCheckVisibility(self):
        return self.m_bCheckVisibile

    def setCheckVisibility(self, bCheck):
        self.m_bCheckVisible = bCheck

    def getRangeMakerCallback(self):
        return self.m_pRangeMakerCallback

    def setRangeMakerCallback(self, pRangeMakerCallback):
        self.m_pRangeMakerCallback = pRangeMakerCallback

    def getRangePlotList(self, pCastingGroup, pCastingPlot):
        if (self.m_pRangePlotList):
            return self.m_pRangePlotList
        else:
            pRangePlotList = []
            if (self.getRangeMakerCallback()):
                pRangePlotList = self.getRangeMakerCallback()(self.m_pData)
            self.setRangePlotList(pRangePlotList)
            return self.m_pRangePlotList

    def setRangePlotList(self, pRangePlotList):
        self.m_pRangePlotList = pRangePlotList

    def getRangeAffectRule(self):
        return self.m_iRangeAffectRule

    def setRangeAffectRule(self, iNewRule):
        self.m_iRangeAffectRule = iNewRule
        
    def setTargetWithData(self, iData1, iData2=-1, iData3=-1):
        self.m_pData = CyMap().plot(iData1, iData2)

    def getData1(self):
        return self.m_pData.getX()

    def getData2(self):
        return self.m_pData.getY()

    def getData1(self):
        return self.m_pData.getX()

    def getData2(self):
        return self.m_pData.getY()

class GSSinglePlotTarget(GSPlotTarget):
    def isAffectedTarget(self, pCastingGroup, pCastingPlot, pTarget):
        if self.isTargettable(pCastingGroup, pCastingPlot, pTarget, False):
            return True

    def getSecondaryTargetList(self, pCastingGroup, pCastingPlot):
        pAffectedList = []
        pUnaffectedList = []
        if ( self.isAffectedTarget(pCastingGroup, pCastingPlot, self.m_pData) ):
            pAffectedList.append(self.m_pData)
        else:
            pUnaffectedList.append(self.m_pData)

        return (pAffectedList, pUnaffectedList)

class GSSquarePlotTarget(GSPlotTarget):
    def __init__(self, pTargetTestCallback, iSquareSize):
        GSPlotTarget.__init__(self, pTargetTestCallback)
        self.m_iSquareSize = iSquareSize

    def setSquareSize(self, iSquareSize):
        self.m_iSquareSize = iSquareSize

    def isAffectedTarget(self, pCastingGroup, pCastingPlot, pTarget):
        if (not self.isTargettable(pCastingGroup, pCastingPlot, pTarget, False)):
            return False

        iSquareCenterX = self.m_pData.getX()
        iSquareCenterY = self.m_pData.getY()
        iSquareSize = self.m_iSquareSize
        if (abs(pTarget.getX() - iSquareCenterX) <= iSquareSize and
            abs(pTarget.getY() - iSquareCenterY) <= iSquareSize):
            return True

    def getSecondaryTargetList(self, pCastingGroup, pCastingPlot):
        pAffectedList = []
        pUnaffectedList = []
        iSquareSize = self.m_iSquareSize
        pTargetPlot = self.m_pData
        # Just test the targetted plot, and set all to the result
        if (self.getRangeAffectRule() == PLOT_RANGE_AFFECT_RULE_SELECTED):
            if (self.isAffectedTarget(pCastingGroup, pCastingPlot, self.m_pData)):
                for iX in range(-1 * iSquareSize, iSquareSize + 1):
                    for iY in range(-1 * iSquareSize, iSquareSize + 1):
                        pAffectedList.append( CyMap().plot(iX + pTargetPlot.getX(), iY + pTargetPlot.getY() ) )
            else:
                for iX in range(-1 * iSquareSize, iSquareSize + 1):
                    for iY in range(-1 * iSquareSize, iSquareSize + 1):
                        pUnaffectedList.append( CyMap().plot(iX + pTargetPlot.getX(), iY + pTargetPlot.getY() ) )
                
        # Test each plot, and if any one plot fails, then they will all fail.
        elif (self.getRangeAffectRule() == PLOT_RANGE_AFFECT_RULE_GLOBAL):
            pPlotList = []
            bPass = True
            for iX in range(-1 * iSquareSize, iSquareSize + 1):
                for iY  in range(-1 * iSquareSize, iSquareSize + 1):
                    pPlot = CyMap().plot(iX + pTargetPlot.getX(), iY + pTargetPlot.getY() )
                    pPlotList.append(pPlot)
                    if (not self.isAffectedTarget(pCastingGroup, pCastingPlot, pPlot) ):
                        bPass = False

            if bPass:
                pAffectedList = pPlotList
            else:
                pUnaffectedList = pPlotList
            

        # Test each plot individually
        elif (self.getRangeAffectRule() == PLOT_RANGE_AFFECT_RULE_INDIVIDUAL):
            for iX in range(-1 * iSquareSize, iSquareSize + 1):
                for iY in range(-1 * iSquareSize, iSquareSize + 1):
                    pTestPlot = CyMap().plot(iX + pTargetPlot.getX(), iY + pTargetPlot.getY() )
                    if (self.isAffectedTarget(pCastingGroup, pCastingPlot, pTestPlot) ):
                        pAffectedList.append(pTestPlot)
                    else:
                        pUnaffectedList.append(pTestPlot)

        else:
            CvUtil.pyAssert(false, "Error making range targets... No known range affect value")

        return (pAffectedList, pUnaffectedList)

class GSUnitTarget(GSBase):
    def setTargetWithData(self, iData1, iData2=-1, iData3=-1):
        self.m_pData = iData1

    def getData1(self):
        return self.m_pData

    def parseInfo(self, bStrategyOption):
        return CyGameTextMgr().getUnitHelp(self.m_pData, False, bStrategyOption, False, None)

    def getBars(self):
        return []

    def getButton(self):
        return gc.getUnitInfo(self.m_pData).getButton()

    def getTargettableList(self, pCastingGroup, pCastingPlot, pPlotList): # pPlotList ignored
        pList = []
        for iI in range(0, gc.getNumUnitInfos()):
            if (self.isTargettable(pCastingGroup, pCastingPlot, iI, True)):
                pList.append(iI)

        return pList

    def isAffectedTarget(self, pCastingGroup, pCastingPlot, pTarget):
        return pTarget == self.m_pData

    def getSecondaryTargetList(self, pCastingGroup, pCastingPlot):
        pAffectedList = []
        pUnaffectedList = []
        if (self.isAffectedTarget(pCastingGroup, pCastingPlot, self.m_pData)):
            pAffectedList.append(self.m_pData)

        return (pAffectedList, pUnaffectedList)

class GSSpecificUnitTarget(GSBase):
    def setTargetWithData(self, iData1, iData2=-1, iData3=-1):
        self.m_pData = gc.getPlayer(iData1).getUnit(iData2)

    def getData1(self):
        return self.m_pData.getOwner()

    def getData2(self):
        return self.m_pData.getID()

    def parseInfo(self, bStrategyOption):
        return CyGameTextMgr().getSpecificUnitHelp(self.m_pData, False, False)

    def getBars(self):
        bShowHealth = False
        if (self.m_pData.isFighting()):
                bShowHealth = False
        elif (self.m_pData.getDomainType() == DomainTypes.DOMAIN_AIR):
                bShowHealth = self.m_pData.canAirAttack()
        else:
                bShowHealth = self.m_pData.canFight()
        
        if bShowHealth:
            if (self.m_pData.getDamage() >= ((self.m_pData.maxHitPoints() * 2) / 3)):
                return [(self.m_pData.currHitPoints(), self.m_pData.maxHitPoints(), "COLOR_RED")]
            elif (self.m_pData.getDamage() >= (self.m_pData.maxHitPoints() / 3)):
                return [(self.m_pData.currHitPoints(), self.m_pData.maxHitPoints(), "COLOR_YELLOW")]
            else:
                return [(self.m_pData.currHitPoints(), self.m_pData.maxHitPoints(), "COLOR_GREEN")]

    def getButton(self):
        return gc.getUnitInfo(self.m_pData.getUnitType()).getButton()

    def getTargettableList(self, pCastingGroup, pCastingPlot, pPlotList): 
        pList = []
        for pPlot in pPlotList:
            for iI in range(0, pPlot.getNumUnits()):
                pUnit = pPlot.getUnit(iI)
                if (self.isTargettable(pCastingGroup, pCastingPlot, pUnit, True)):
                    pList.append(pUnit)

        return pList

    def isAffectedTarget(self, pCastingGroup, pCastingPlot, pTarget):
        return pTarget == self.m_pData

    def getSecondaryTargetList(self, pCastingGroup, pCastingPlot):
        pAffectedList = []
        pUnaffectedList = []
        if (self.isAffectedTarget(pCastingGroup, pCastingPlot, self.m_pData) ):
            pAffectedList.append(self.m_pData)
        return (pAffectedList, pUnaffectedList)

class GSSpecificBuildTarget(GSBase):
    def setTargetWithData(self, iData1, iData2=-1, iData3=-1):
        self.m_pData = iData1

    def getData1(self):
        return self.m_pData

    def getData2(self):
        return -1

    def parseInfo(self, bStrategyOption):
        return gc.getBuildInfo(self.m_pData).getDescription()

    def getBars(self):
        return []

    def getButton(self):
        return gc.getBuildInfo(self.m_pData).getButton()

    def getTargettableList(self, pCastingGroup, pCastingPlot, pPlotList):
        pList = []
        for iI in range(0, gc.getNumBuildInfos()):
            for pPlot in pPlotList:
                if self.isTargettable(pCastingGroup, pPlot, iI, True):
                     pList.append(iI)
                     break

        return pList

    def isAffectedTarget(self, pCastingGroup, pCastingPlot, pTarget):
        if (pTarget == self.m_pData):
            return True

    def getSecondaryTargetList(self, pCastingGroup, pCastingPlot):
        pAffectedList = []
        pUnaffectedList = []
        if (self.isAffectedTarget(pCastingGroup, pCastingPlot, self.m_pData) ):
            pAffectedList.append(self.m_pData)
        return (pAffectedList, pUnaffectedList)

class GSBuildingTarget(GSBase):
    def setTargetWithData(self, iData1, iData2=-1, iData3=-1):
        self.m_pData = iData1

    def getData1(self):
        return self.m_pData

    def getData2(self):
        return -1

    def parseInfo(self, bStrategyOption):
        return gc.getBuildingInfo(self.m_pData).getDescription()

    def getBars(self):
        return []

    def getButton(self):
        return gc.getBuildingInfo(self.m_pData).getButton()

    def getTargettableList(self, pCastingGroup, pCastingPlot, pPlotList): # pPlotList ignored
        pList = []
        for iI in range(0, gc.getNumBuildingInfos()):
            if self.isTargettable(pCastingGroup, pCastingPlot, iI, True):
                pList.append(iI)

        return pList

    def isAffectedTarget(self, pCastingGroup, pCastingPlot, pTarget):
        if (pTarget == self.m_pData):
            return True

    def getSecondaryTargetList(self, pCastingGroup, pCastingPlot):
        pAffectedList = []
        pUnaffectedList = []
        if (self.isAffectedTarget(pCastingGroup, pCastingPlot, self.m_pData) ):
            pAffectedList.append(self.m_pData)
        return (pAffectedList, pUnaffectedList)
